package com.daiziru.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.db.nosql.redis.RedisDS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.daiziru.VO.PostVO;
import com.daiziru.common.constant.RedisConstants;
import com.daiziru.entity.MPost;
import com.daiziru.mapper.MPostMapper;
import com.daiziru.service.MPostService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.daiziru.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.lang.model.element.VariableElement;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author wuyan
 * @since 2022-12-07
 */
@Service
public class MPostServiceImpl extends ServiceImpl<MPostMapper, MPost> implements MPostService {


    @Autowired
    MPostMapper postMapper;

    @Autowired
    RedisUtil redisUtil;

    /**
     *
     * @param page 分页相关信息【当前页，每页大小】
     * @param categoryId 是否分类
     * @param userId 用户id
     * @param level 是否置顶,默认为0不置顶， 大于0则为置顶帖子，越大越在前面
     * @param recommend 是否精选
     * @param order 排序类型
     * @return
     */
    @Override
    public IPage<PostVO> selectByPage(Page page, Long categoryId, Long userId, Integer level, Boolean recommend, String order) {
        if(level == null) level  = -1;
        QueryWrapper wrapper = new QueryWrapper<MPost>()
                .eq(categoryId != null, "category_id", categoryId)
                .eq(userId != null, "user_id", userId)
                .eq(level == 0, "level", 0) //等于0，只查询非置顶的帖子
                .gt(level > 0, "level", 0) //查询置顶帖子
                .orderByDesc(order != null, order);
        return postMapper.selectPosts(page, wrapper);
    }

    @Override
    public PostVO selectOnePost(QueryWrapper<MPost> wrapper) {
        return postMapper.selectOnePost(wrapper);
    }

    /**
     * 本周热议相关信息初始化
     * 从当前一天，往前推7天
     */
    @Override
    @Scheduled(cron = "0 59 11 ? * * ") //每天11:59分更新本周热议信息
    public void initWeekRank() {
        // 获取7天的发表的文章 当前12号。7天内就是往前推6天，也就是6号到12号共7天
        List<MPost> posts = this.list(new QueryWrapper<MPost>()
                .ge("created", DateUtil.offsetDay(new Date(), -6))
                .select("id", "title", "user_id", "comment_count", "view_count", "created")
        );
        // 初始化7天内每一天文章的总评论量
        for (MPost post : posts) {
            //day:rank:20221109
            String key = RedisConstants.DAY_RANK + DateUtil.format(post.getCreated(), DatePattern.PURE_DATE_FORMAT);
            //zadd day:3(key) 10(评论数) post:1(文章id)
            redisUtil.zSet(key, post.getId(), post.getCommentCount());
            /**
             * 设置自动过期时间
             * 7天后自动过期(比如15号发表的文章，到了18号， 还剩余4天的存活时间，7-（18-15）=4)
             */
            long between = DateUtil.between(new Date(), post.getCreated(), DateUnit.DAY);
            long expireTime = (7 - between) * 24 * 60 * 60; // 有效时间
            redisUtil.expire(key, expireTime);
            // 缓存文章的一些基本信息（id，标题，评论数量，作者）
            this.hashCachePostIdAndTitle(post, expireTime);
        }
        // 7天内的所有文章的评论数做并集
        this.zUnionAndStoreLast7DayForWeekRank();
    }

    @Override
    public void incrCommentCountAndUnionForWeekRank(long postId, boolean isIncr) {
        String  currentKey = RedisConstants.DAY_RANK + DateUtil.format(new Date(), DatePattern.PURE_DATE_FORMAT);
        redisUtil.zIncrementScore(currentKey, postId, isIncr? 1: -1);
        MPost post = this.getById(postId);
        // 7天后自动过期(15号发表，7-（18-15）=4)
        long between = DateUtil.between(new Date(), post.getCreated(), DateUnit.DAY);
        long expireTime = (7 - between) * 24 * 60 * 60; // 有效时间
        // 缓存这篇文章的基本信息
        this.hashCachePostIdAndTitle(post, expireTime);
        // 重新做并集
        this.zUnionAndStoreLast7DayForWeekRank();
    }

    /**
     * 文章的访问量设置到缓存
     * @param vo
     */
    @Override
    public void putViewCount(PostVO vo) {
        String key = RedisConstants.VIEWCOUNT_POST + vo.getId();
        //1、从缓存中获取viewCount
        Integer viewCount = (Integer) redisUtil.hget(key, "post:viewCount");
        // 2、如果没有，就先从实体里面获取，再加一
        if (viewCount != null) {
            vo.setViewCount(viewCount + 1);
        } else {
            vo.setViewCount(vo.getViewCount() + 1);
        }
        //3、更新到缓存,并设置过期时间为1h
        redisUtil.hset(key,"post:viewCount", vo.getViewCount());
        redisUtil.expire(key,1 * 60 * 60);


    }

    /**
     * 把7天内每一天的每一篇文章的评论数都统计合并
     */
    public void zUnionAndStoreLast7DayForWeekRank() {
        //当前日期
        String  currentKey = RedisConstants.DAY_RANK + DateUtil.format(new Date(), DatePattern.PURE_DATE_FORMAT);
        //合并后存到改key
        String destKey = RedisConstants.WEEK_RANK;
        List<String> otherKeys = new ArrayList<>();
        //当前的日前往前的每一天
        for(int i=-6; i < 0; i++) {
            String temp = RedisConstants.DAY_RANK +
                    DateUtil.format(DateUtil.offsetDay(new Date(), i), DatePattern.PURE_DATE_FORMAT);
            otherKeys.add(temp);
        }
        //zunionstore week:rank 3 day:1 day:2 day:3
        redisUtil.zUnionAndStore(currentKey, otherKeys, destKey);
    }

    /**
     * 缓存文章的基本信息
     * @param post 文章的基本信息
     * @param expireTime
     */
    public void hashCachePostIdAndTitle(MPost post, long expireTime) {
        String key = RedisConstants.RANK_POST + post.getId();
        boolean hasKey = redisUtil.hasKey(key);
        if(!hasKey) {
            redisUtil.hset(key, "post:id", post.getId(), expireTime);
            redisUtil.hset(key, "post:title", post.getTitle(), expireTime);
            redisUtil.hset(key, "post:commentCount", post.getCommentCount(), expireTime);
            redisUtil.hset(key, "post:content", post.getContent(), expireTime);
            redisUtil.hset(key, "post:title", post.getTitle(), expireTime);
            redisUtil.hset(key, "post:created", post.getCreated(), expireTime);
        }
    }
}
